import sys
import os
import time
import math
import re,json

from collections import defaultdict

from XmlReaderExpat import XmlParserSax, XmlElement
from enum import Enum
import codecs

BUILD_DATE = "2018-8-9 11:15:34"
#ROOT_PATH = os.path.split(__file__)[0] #非main入口 得到的路径是py环境路径
ROOT_PATH = os.getcwd() #可以得到准确的工作环境路径
#print("ROOT_PATH",ROOT_PATH,"os.getcwd:",os.getcwd()) 
ASSETS_PATH = os.path.join(ROOT_PATH,"assets")
CONFIG_PATH = os.path.join(ASSETS_PATH,"config")
TEMP_PATH = os.path.join(ASSETS_PATH,"template")
VERSION_PATH = os.path.join(ASSETS_PATH,"file_version_info.txt")
regFnName = re.compile(r"#[a-zA-Z0-9_]+#")
regName = re.compile(r"^n\d+$")
regModuleName = re.compile(r"^[a-zA-Z]+$") 
regClsName = re.compile(r"^[a-zA-Z_]+[a-zA-Z_0-9]*$") 
xmlParse = XmlParserSax()
dataTypeConfig = None
toolConfig = None
versionInf = None
fnTempMap = defaultdict(str)

versionCN = {
    "CompanyName":"公司名",
    "FileVersion":"文件版本",
    "LegalCopyright":"版权",
    "OriginalFilename":"原始文件名",
    "ProductName":"产品名",
    "ProductVersion":"产品版本"
    
}

aboutCfgMap={
    "0":[
            "设置好要生成代码的相关信息",
            "直接把GUI组件的xml文件拖进来即可生成TS",
            "生成已存在的TS文件手写代码会被覆盖(慎重)",
            "只会生成自定义名称的元件",
            "按钮只会生成btn_命名开头的元件,过滤掉btn_close",
            "文本只会生成txt_命名开头的元件",
            "BaseItem只会生成命名以baseitem开头的",
            "除了BaseItem不会生成组件里嵌入的组件(组件可以单独再生成一个类)",
            "不勾选忽略模块名的话，如果模块不存在会自动创建",
            "设置好模块根目录后，输入模块前几个字母会自动搜索",
            "每次打开会自动扫描assets/template基类目录，可以自己增减基类",
            "可以自己修改基类模板添加必要的信息"
        ],
    "1":[
            "把一个Excel数据导出到json文件",           
        ]
}

class EDisplayType(Enum):
    image = 0
    Button = 1
    text = 2
    richtext = 3
    loader = 4
    component = 5
    controller = 6
    list = 7
    baseitem = 8
    group = 9

class EToolCfg(Enum):
    modulePath = 1 #模块目录
    author = 2 #作者
    autoBtn = 3 #自动按钮点击事件
    autoList = 4 #自动list点击事件
    fieldPro = 5 #字段限定符
    autoCtrl = 6 #自动为controller添加点击事件
    moduleName = 7 #记录上次使用的模块名
    fieldStyle = 8  #是否驼峰变量名
    autoMkProxy = 9 #自动创建Proxy代理类
    autoMkCtrl = 10 #自动创建controller类
    autoMkCache = 11 #自动创建cache类

class EFnTemp(Enum):
    """
    功能代码模板文件名枚举:
        点击函数模板
        proxy和controller类模板    
    Arguments:
        object {[type]} -- [description]
    """
    btnFn = 1
    listFn = 2
    ctrlFn = 3
    controller = 4
    proxy = 5
    cache = 6


def getToolCfgUrl():
    jsonUrl = os.path.join(CONFIG_PATH,"tool.conf")
    return jsonUrl

def initToolCfg():
    """
    初始化工具配置 tool.conf
    """
    global toolConfig
    jsonUrl = getToolCfgUrl()
    with open(jsonUrl,"r",encoding="utf8") as f:
        toolConfig = json.load(f)
        
def getToolCfg(key):
    return toolConfig.get(key,"")

def initVersionInf(versionfile):
    global versionInf
    vsArr = None
    with codecs.open(versionfile, 'rU', 'utf-8') as fp:        
        txt = fp.read()
        reg = re.compile(r"StringTable\([\s\S]+?\]\)\,") # [\s\S] 可以匹配任意字符 . 只能匹配非换行的任意字符
        mobj = reg.search(txt)
        verStr = mobj.group()
        verStr = verStr[:len(verStr)-4]
        verStr = verStr.replace("StringTable","")
        verStr = verStr.replace("StringStruct","")        
        verStr = re.sub("\s","",verStr) #去掉换行符 空白        
        vs = eval(verStr)
        vsArr = vs[1]            
    versionInf = vsArr

def getVersionInfStr():
    vs = versionInf
    n = len(vs)
    verStr = ""
    for i in range(n):
        s = vs[i]
        cn = versionCN.get(s[0],None)
        if cn:            
            verStr += cn + ":"+s[1]+"<br>"
    return verStr+BUILD_DATE

def initDataTypeCfg():
    """
    初始化数据类型的配置 dataType.json
    """
    global dataTypeConfig
    jsonUrl = os.path.join(CONFIG_PATH,"dataType.json")
    with open(jsonUrl,"r",encoding="utf8") as f:
        dataTypeConfig = json.load(f)


def setToolCfg(key,value,isSave=True):
    toolConfig[key] = value
    if isSave:        
        jsonUrl = getToolCfgUrl()
        with open(jsonUrl,"w",encoding="utf8") as f:
            json.dump(toolConfig,f,indent=2,ensure_ascii=False) # ensure_ascii 避免英文乱码 indent格式化输出

def getDataTypeStr(name):
    dts = dataTypeConfig['dt'].get(name,"any")    
    return dts

def getDataVarAs(name):
    vas = dataTypeConfig['asVar'].get(name,"")
    return vas

#获取UI函数名
def getFuncName(name):
    vas = dataTypeConfig['funcName'].get(name,"")
    if not vas:
        vas = name
    return vas

def writeFile(fileName,content):    
    with open(fileName,"w",encoding="utf8") as f:
        f.write(content)


def getTemp(tempFile):
    """
        获取基类模板
    Arguments:
        tempFile {[str]} -- [无后缀的模板文件名]
    """
    tempFile = tempFile+".temp"
    retStr = ""
    path = os.path.join(TEMP_PATH,tempFile)
    if os.path.exists(path):
        with open(path,"r",encoding="utf8") as f:
            retStr = f.read()
    return retStr

def getFnTemp(tempFile):
    """
    获取功能模板文件内容
    
    Arguments:
        tempFile {[str]} -- [文件名 无后缀]
    """
    retStr = fnTempMap.get(tempFile)
    if not retStr:        
        tempFile = tempFile+".temp"    
        path = os.path.join(ASSETS_PATH,"functemp",tempFile)
        if os.path.exists(path):
            with open(path,"r",encoding="utf8") as f:
                retStr = f.read()
                fnTempMap[tempFile] = retStr             
    return retStr

def getFnTempName(tempFile):
    """获取函数目标里面的函数名称
    
    Arguments:
        tempFile {[str]} -- [不带后缀的目标文件名]
    """
    retStr = getFnTemp(tempFile)
    mobj = regFnName.search(retStr)
    fnStr = "onTestFn"
    if mobj:
        fnStr = mobj.group()
        fnStr = fnStr.replace("#","")
    return fnStr

def getTempList():
    """获取所有基类列表
    """
    tempList = [ x.split(".")[0] for x in os.listdir(TEMP_PATH) ]
    tempList.sort()    
    return tempList

def isCreateXmlNode(xmlEle):
    '''
    是否是需要生成代码的组件
    '''
    #print("isCreateXmlNode xmlEle.name",xmlEle.name)
    xmlNode = None
    mobj = regName.match(xmlEle.attr['name'])
    if not mobj:        
        if xmlEle.name==EDisplayType.component.name:        
            if isButton(xmlEle):                                    
                xmlNode = xmlEle
                xmlNode.name = EDisplayType.Button.name
            elif isBaseItem(xmlEle):
                xmlNode = xmlEle
                xmlNode.name = EDisplayType.baseitem.name

        elif xmlEle.name==EDisplayType.text.name or xmlEle.name==EDisplayType.richtext.name:
            if xmlEle.attr and xmlEle.attr['name'].startswith("txt_"):
                xmlNode = xmlEle
        elif xmlEle.name==EDisplayType.controller.name:
            if xmlEle.attr.get("name","")!="button": # 过滤掉名字为 button 的 controller
                xmlNode = xmlEle             
        else:        
            xmlNode = xmlEle

    return xmlNode

def isButton(xmlEle):    
    btnName = xmlEle.attr.get("name","")    
    flag = False
    if str(btnName).startswith("btn_") and btnName!="btn_close": #忽略所有 btn_close         
        flag = True #len(xmlEle.children)==1 and xmlEle.children[0].name==EDisplayType.Button.name  
    return flag 
def isBaseItem(xmlEle):
    name = xmlEle.attr.get("name","")
    flag = name.lower().startswith("baseitem")
    return flag  

def xmlToTsInf(xmlOrFile):
    print("xmlOrFile",xmlOrFile)
    xmlParse.updateByXml(xmlOrFile, False)
    controllers = xmlParse.xmlData.get("controller",[])
    displayList = xmlParse.xmlData.get("displayList",[])
    
    displayMap = defaultdict(list)
    if controllers and len(controllers)>0:
        displayMap[EDisplayType.controller.name] = []
        for c in controllers:
            c = isCreateXmlNode(c)
            if c:
                displayMap[EDisplayType.controller.name].append(c)

    if len(displayList)>0:        
        displayList = displayList[0]
        num = len(displayList.children)
        for i in range(num):
            node = isCreateXmlNode(displayList.children[i])
            if node:
                if not displayMap[node.name]:
                    displayMap[node.name] = []
                displayMap[node.name].append(node)

    #print("displayMap",displayMap)
    #print("displayList",displayList)
    return displayMap

def get_now_str():
    timestamp = time.time() #获取当前时间戳        
    structTime = time.localtime(timestamp) #把时间戳转换成本地时间信息
    fmt = "%Y-%m-%d %H:%M:%S" #格式化字符串参见:timeFmt.txt
    fmTimeStr=time.strftime(fmt,structTime) # %H 24小时制的格式化,%I 12小时制的格式化
    return fmTimeStr

def getAboutStr(index):
    index = str(index)
    descArr = aboutCfgMap[index]
    if descArr:
        length = len(descArr)
        s = ""
        for i in range(length):
            s+=str(i+1)+"."+descArr[i]+"<br>"
        return s
    return ""
def write_log(logStr):
    url = os.path.join(ROOT_PATH,"h5gui.log")
    with open(url,"a",encoding="utf8") as f:
        fmTimeStr = get_now_str()
        f.write(fmTimeStr+"\r"+logStr+"\r")

